// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// package mips -- go2cs converted at 2022 March 13 06:24:44 UTC
// import "cmd/compile/internal/mips" ==> using mips = go.cmd.compile.@internal.mips_package
// Original source: C:\Program Files\Go\src\cmd\compile\internal\mips\ssa.go
namespace go.cmd.compile.@internal;

using math = math_package;

using @base = cmd.compile.@internal.@base_package;
using ir = cmd.compile.@internal.ir_package;
using logopt = cmd.compile.@internal.logopt_package;
using ssa = cmd.compile.@internal.ssa_package;
using ssagen = cmd.compile.@internal.ssagen_package;
using types = cmd.compile.@internal.types_package;
using obj = cmd.@internal.obj_package;
using mips = cmd.@internal.obj.mips_package;


// isFPreg reports whether r is an FP register

public static partial class mips_package {

private static bool isFPreg(short r) {
    return mips.REG_F0 <= r && r <= mips.REG_F31;
}

// isHILO reports whether r is HI or LO register
private static bool isHILO(short r) {
    return r == mips.REG_HI || r == mips.REG_LO;
}

// loadByType returns the load instruction of the given type.
private static obj.As loadByType(ptr<types.Type> _addr_t, short r) => func((_, panic, _) => {
    ref types.Type t = ref _addr_t.val;

    if (isFPreg(r)) {
        if (t.Size() == 4) { // float32 or int32
            return mips.AMOVF;
        }
        else
 { // float64 or int64
            return mips.AMOVD;
        }
    }
    else
 {
        switch (t.Size()) {
            case 1: 
                           if (t.IsSigned()) {
                               return mips.AMOVB;
                           }
                           else
                {
                               return mips.AMOVBU;
                           }
                break;
            case 2: 
                           if (t.IsSigned()) {
                               return mips.AMOVH;
                           }
                           else
                {
                               return mips.AMOVHU;
                           }
                break;
            case 4: 
                return mips.AMOVW;
                break;
        }
    }
    panic("bad load type");
});

// storeByType returns the store instruction of the given type.
private static obj.As storeByType(ptr<types.Type> _addr_t, short r) => func((_, panic, _) => {
    ref types.Type t = ref _addr_t.val;

    if (isFPreg(r)) {
        if (t.Size() == 4) { // float32 or int32
            return mips.AMOVF;
        }
        else
 { // float64 or int64
            return mips.AMOVD;
        }
    }
    else
 {
        switch (t.Size()) {
            case 1: 
                return mips.AMOVB;
                break;
            case 2: 
                return mips.AMOVH;
                break;
            case 4: 
                return mips.AMOVW;
                break;
        }
    }
    panic("bad store type");
});

private static void ssaGenValue(ptr<ssagen.State> _addr_s, ptr<ssa.Value> _addr_v) {
    ref ssagen.State s = ref _addr_s.val;
    ref ssa.Value v = ref _addr_v.val;


    if (v.Op == ssa.OpCopy || v.Op == ssa.OpMIPSMOVWreg)
    {
        var t = v.Type;
        if (t.IsMemory()) {
            return ;
        }
        var x = v.Args[0].Reg();
        var y = v.Reg();
        if (x == y) {
            return ;
        }
        var @as = mips.AMOVW;
        if (isFPreg(x) && isFPreg(y)) {
            as = mips.AMOVF;
            if (t.Size() == 8) {
                as = mips.AMOVD;
            }
        }
        var p = s.Prog(as);
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = x;
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = y;
        if (isHILO(x) && isHILO(y) || isHILO(x) && isFPreg(y) || isFPreg(x) && isHILO(y)) { 
            // cannot move between special registers, use TMP as intermediate
            p.To.Reg = mips.REGTMP;
            p = s.Prog(mips.AMOVW);
            p.From.Type = obj.TYPE_REG;
            p.From.Reg = mips.REGTMP;
            p.To.Type = obj.TYPE_REG;
            p.To.Reg = y;
        }
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSMOVWnop)
    {
        goto __switch_break0;
    }
    if (v.Op == ssa.OpLoadReg)
    {
        if (v.Type.IsFlags()) {
            v.Fatalf("load flags not implemented: %v", v.LongString());
            return ;
        }
        var r = v.Reg();
        p = s.Prog(loadByType(_addr_v.Type, r));
        ssagen.AddrAuto(_addr_p.From, v.Args[0]);
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = r;
        if (isHILO(r)) { 
            // cannot directly load, load to TMP and move
            p.To.Reg = mips.REGTMP;
            p = s.Prog(mips.AMOVW);
            p.From.Type = obj.TYPE_REG;
            p.From.Reg = mips.REGTMP;
            p.To.Type = obj.TYPE_REG;
            p.To.Reg = r;
        }
        goto __switch_break0;
    }
    if (v.Op == ssa.OpStoreReg)
    {
        if (v.Type.IsFlags()) {
            v.Fatalf("store flags not implemented: %v", v.LongString());
            return ;
        }
        r = v.Args[0].Reg();
        if (isHILO(r)) { 
            // cannot directly store, move to TMP and store
            p = s.Prog(mips.AMOVW);
            p.From.Type = obj.TYPE_REG;
            p.From.Reg = r;
            p.To.Type = obj.TYPE_REG;
            p.To.Reg = mips.REGTMP;
            r = mips.REGTMP;
        }
        p = s.Prog(storeByType(_addr_v.Type, r));
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = r;
        ssagen.AddrAuto(_addr_p.To, v);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSADD || v.Op == ssa.OpMIPSSUB || v.Op == ssa.OpMIPSAND || v.Op == ssa.OpMIPSOR || v.Op == ssa.OpMIPSXOR || v.Op == ssa.OpMIPSNOR || v.Op == ssa.OpMIPSSLL || v.Op == ssa.OpMIPSSRL || v.Op == ssa.OpMIPSSRA || v.Op == ssa.OpMIPSADDF || v.Op == ssa.OpMIPSADDD || v.Op == ssa.OpMIPSSUBF || v.Op == ssa.OpMIPSSUBD || v.Op == ssa.OpMIPSMULF || v.Op == ssa.OpMIPSMULD || v.Op == ssa.OpMIPSDIVF || v.Op == ssa.OpMIPSDIVD || v.Op == ssa.OpMIPSMUL)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[1].Reg();
        p.Reg = v.Args[0].Reg();
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSSGT || v.Op == ssa.OpMIPSSGTU)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[0].Reg();
        p.Reg = v.Args[1].Reg();
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSSGTzero || v.Op == ssa.OpMIPSSGTUzero)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[0].Reg();
        p.Reg = mips.REGZERO;
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSADDconst || v.Op == ssa.OpMIPSSUBconst || v.Op == ssa.OpMIPSANDconst || v.Op == ssa.OpMIPSORconst || v.Op == ssa.OpMIPSXORconst || v.Op == ssa.OpMIPSNORconst || v.Op == ssa.OpMIPSSLLconst || v.Op == ssa.OpMIPSSRLconst || v.Op == ssa.OpMIPSSRAconst || v.Op == ssa.OpMIPSSGTconst || v.Op == ssa.OpMIPSSGTUconst)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_CONST;
        p.From.Offset = v.AuxInt;
        p.Reg = v.Args[0].Reg();
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSMULT || v.Op == ssa.OpMIPSMULTU || v.Op == ssa.OpMIPSDIV || v.Op == ssa.OpMIPSDIVU) 
    {
        // result in hi,lo
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[1].Reg();
        p.Reg = v.Args[0].Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSMOVWconst)
    {
        r = v.Reg();
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_CONST;
        p.From.Offset = v.AuxInt;
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = r;
        if (isFPreg(r) || isHILO(r)) { 
            // cannot move into FP or special registers, use TMP as intermediate
            p.To.Reg = mips.REGTMP;
            p = s.Prog(mips.AMOVW);
            p.From.Type = obj.TYPE_REG;
            p.From.Reg = mips.REGTMP;
            p.To.Type = obj.TYPE_REG;
            p.To.Reg = r;
        }
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSMOVFconst || v.Op == ssa.OpMIPSMOVDconst)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_FCONST;
        p.From.Val = math.Float64frombits(uint64(v.AuxInt));
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSCMOVZ)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[2].Reg();
        p.Reg = v.Args[1].Reg();
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSCMOVZzero)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[1].Reg();
        p.Reg = mips.REGZERO;
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSCMPEQF || v.Op == ssa.OpMIPSCMPEQD || v.Op == ssa.OpMIPSCMPGEF || v.Op == ssa.OpMIPSCMPGED || v.Op == ssa.OpMIPSCMPGTF || v.Op == ssa.OpMIPSCMPGTD)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[0].Reg();
        p.Reg = v.Args[1].Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSMOVWaddr)
    {
        p = s.Prog(mips.AMOVW);
        p.From.Type = obj.TYPE_ADDR;
        p.From.Reg = v.Args[0].Reg();
        @string wantreg = default; 
        // MOVW $sym+off(base), R
        // the assembler expands it as the following:
        // - base is SP: add constant offset to SP (R29)
        //               when constant is large, tmp register (R23) may be used
        // - base is SB: load external address with relocation
        switch (v.Aux.type()) {
            case ptr<obj.LSym> _:
                wantreg = "SB";
                ssagen.AddAux(_addr_p.From, v);
                break;
            case ptr<ir.Name> _:
                wantreg = "SP";
                ssagen.AddAux(_addr_p.From, v);
                break;
            case 
                wantreg = "SP";
                p.From.Offset = v.AuxInt;
                break;
            default:
            {
                v.Fatalf("aux is of unknown type %T", v.Aux);
                break;
            }
        }
        {
            var reg = v.Args[0].RegName();

            if (reg != wantreg) {
                v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg);
            }

        }
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSMOVBload || v.Op == ssa.OpMIPSMOVBUload || v.Op == ssa.OpMIPSMOVHload || v.Op == ssa.OpMIPSMOVHUload || v.Op == ssa.OpMIPSMOVWload || v.Op == ssa.OpMIPSMOVFload || v.Op == ssa.OpMIPSMOVDload)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_MEM;
        p.From.Reg = v.Args[0].Reg();
        ssagen.AddAux(_addr_p.From, v);
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSMOVBstore || v.Op == ssa.OpMIPSMOVHstore || v.Op == ssa.OpMIPSMOVWstore || v.Op == ssa.OpMIPSMOVFstore || v.Op == ssa.OpMIPSMOVDstore)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[1].Reg();
        p.To.Type = obj.TYPE_MEM;
        p.To.Reg = v.Args[0].Reg();
        ssagen.AddAux(_addr_p.To, v);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSMOVBstorezero || v.Op == ssa.OpMIPSMOVHstorezero || v.Op == ssa.OpMIPSMOVWstorezero)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = mips.REGZERO;
        p.To.Type = obj.TYPE_MEM;
        p.To.Reg = v.Args[0].Reg();
        ssagen.AddAux(_addr_p.To, v);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSMOVBreg || v.Op == ssa.OpMIPSMOVBUreg || v.Op == ssa.OpMIPSMOVHreg || v.Op == ssa.OpMIPSMOVHUreg)
    {
        var a = v.Args[0];
        while (a.Op == ssa.OpCopy || a.Op == ssa.OpMIPSMOVWreg || a.Op == ssa.OpMIPSMOVWnop) {
            a = a.Args[0];
        }
        if (a.Op == ssa.OpLoadReg) {
            t = a.Type;

            if (v.Op == ssa.OpMIPSMOVBreg && t.Size() == 1 && t.IsSigned() || v.Op == ssa.OpMIPSMOVBUreg && t.Size() == 1 && !t.IsSigned() || v.Op == ssa.OpMIPSMOVHreg && t.Size() == 2 && t.IsSigned() || v.Op == ssa.OpMIPSMOVHUreg && t.Size() == 2 && !t.IsSigned()) 
                // arg is a proper-typed load, already zero/sign-extended, don't extend again
                if (v.Reg() == v.Args[0].Reg()) {
                    return ;
                }
                p = s.Prog(mips.AMOVW);
                p.From.Type = obj.TYPE_REG;
                p.From.Reg = v.Args[0].Reg();
                p.To.Type = obj.TYPE_REG;
                p.To.Reg = v.Reg();
                return ;
            else             
        }
        fallthrough = true;
    }
    if (fallthrough || v.Op == ssa.OpMIPSMOVWF || v.Op == ssa.OpMIPSMOVWD || v.Op == ssa.OpMIPSTRUNCFW || v.Op == ssa.OpMIPSTRUNCDW || v.Op == ssa.OpMIPSMOVFD || v.Op == ssa.OpMIPSMOVDF || v.Op == ssa.OpMIPSNEGF || v.Op == ssa.OpMIPSNEGD || v.Op == ssa.OpMIPSSQRTF || v.Op == ssa.OpMIPSSQRTD || v.Op == ssa.OpMIPSCLZ)
    {
        p = s.Prog(v.Op.Asm());
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[0].Reg();
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSNEG) 
    {
        // SUB from REGZERO
        p = s.Prog(mips.ASUBU);
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[0].Reg();
        p.Reg = mips.REGZERO;
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredZero) 
    {
        // SUBU    $4, R1
        // MOVW    R0, 4(R1)
        // ADDU    $4, R1
        // BNE    Rarg1, R1, -2(PC)
        // arg1 is the address of the last element to zero
        long sz = default;
        obj.As mov = default;

        if (v.AuxInt % 4 == 0) 
            sz = 4;
            mov = mips.AMOVW;
        else if (v.AuxInt % 2 == 0) 
            sz = 2;
            mov = mips.AMOVH;
        else 
            sz = 1;
            mov = mips.AMOVB;
                p = s.Prog(mips.ASUBU);
        p.From.Type = obj.TYPE_CONST;
        p.From.Offset = sz;
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = mips.REG_R1;
        var p2 = s.Prog(mov);
        p2.From.Type = obj.TYPE_REG;
        p2.From.Reg = mips.REGZERO;
        p2.To.Type = obj.TYPE_MEM;
        p2.To.Reg = mips.REG_R1;
        p2.To.Offset = sz;
        var p3 = s.Prog(mips.AADDU);
        p3.From.Type = obj.TYPE_CONST;
        p3.From.Offset = sz;
        p3.To.Type = obj.TYPE_REG;
        p3.To.Reg = mips.REG_R1;
        var p4 = s.Prog(mips.ABNE);
        p4.From.Type = obj.TYPE_REG;
        p4.From.Reg = v.Args[1].Reg();
        p4.Reg = mips.REG_R1;
        p4.To.Type = obj.TYPE_BRANCH;
        p4.To.SetTarget(p2);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredMove) 
    {
        // SUBU    $4, R1
        // MOVW    4(R1), Rtmp
        // MOVW    Rtmp, (R2)
        // ADDU    $4, R1
        // ADDU    $4, R2
        // BNE    Rarg2, R1, -4(PC)
        // arg2 is the address of the last element of src
        sz = default;
        mov = default;

        if (v.AuxInt % 4 == 0) 
            sz = 4;
            mov = mips.AMOVW;
        else if (v.AuxInt % 2 == 0) 
            sz = 2;
            mov = mips.AMOVH;
        else 
            sz = 1;
            mov = mips.AMOVB;
                p = s.Prog(mips.ASUBU);
        p.From.Type = obj.TYPE_CONST;
        p.From.Offset = sz;
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = mips.REG_R1;
        p2 = s.Prog(mov);
        p2.From.Type = obj.TYPE_MEM;
        p2.From.Reg = mips.REG_R1;
        p2.From.Offset = sz;
        p2.To.Type = obj.TYPE_REG;
        p2.To.Reg = mips.REGTMP;
        p3 = s.Prog(mov);
        p3.From.Type = obj.TYPE_REG;
        p3.From.Reg = mips.REGTMP;
        p3.To.Type = obj.TYPE_MEM;
        p3.To.Reg = mips.REG_R2;
        p4 = s.Prog(mips.AADDU);
        p4.From.Type = obj.TYPE_CONST;
        p4.From.Offset = sz;
        p4.To.Type = obj.TYPE_REG;
        p4.To.Reg = mips.REG_R1;
        var p5 = s.Prog(mips.AADDU);
        p5.From.Type = obj.TYPE_CONST;
        p5.From.Offset = sz;
        p5.To.Type = obj.TYPE_REG;
        p5.To.Reg = mips.REG_R2;
        var p6 = s.Prog(mips.ABNE);
        p6.From.Type = obj.TYPE_REG;
        p6.From.Reg = v.Args[2].Reg();
        p6.Reg = mips.REG_R1;
        p6.To.Type = obj.TYPE_BRANCH;
        p6.To.SetTarget(p2);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSCALLstatic || v.Op == ssa.OpMIPSCALLclosure || v.Op == ssa.OpMIPSCALLinter)
    {
        s.Call(v);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredWB)
    {
        p = s.Prog(obj.ACALL);
        p.To.Type = obj.TYPE_MEM;
        p.To.Name = obj.NAME_EXTERN;
        p.To.Sym = v.Aux._<ptr<obj.LSym>>();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredPanicBoundsA || v.Op == ssa.OpMIPSLoweredPanicBoundsB || v.Op == ssa.OpMIPSLoweredPanicBoundsC)
    {
        p = s.Prog(obj.ACALL);
        p.To.Type = obj.TYPE_MEM;
        p.To.Name = obj.NAME_EXTERN;
        p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt];
        s.UseArgs(8); // space used in callee args area by assembly stubs
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredPanicExtendA || v.Op == ssa.OpMIPSLoweredPanicExtendB || v.Op == ssa.OpMIPSLoweredPanicExtendC)
    {
        p = s.Prog(obj.ACALL);
        p.To.Type = obj.TYPE_MEM;
        p.To.Name = obj.NAME_EXTERN;
        p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt];
        s.UseArgs(12); // space used in callee args area by assembly stubs
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredAtomicLoad8 || v.Op == ssa.OpMIPSLoweredAtomicLoad32)
    {
        s.Prog(mips.ASYNC);

        obj.As op = default;

        if (v.Op == ssa.OpMIPSLoweredAtomicLoad8) 
            op = mips.AMOVB;
        else if (v.Op == ssa.OpMIPSLoweredAtomicLoad32) 
            op = mips.AMOVW;
                p = s.Prog(op);
        p.From.Type = obj.TYPE_MEM;
        p.From.Reg = v.Args[0].Reg();
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg0();

        s.Prog(mips.ASYNC);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredAtomicStore8 || v.Op == ssa.OpMIPSLoweredAtomicStore32)
    {
        s.Prog(mips.ASYNC);

        op = default;

        if (v.Op == ssa.OpMIPSLoweredAtomicStore8) 
            op = mips.AMOVB;
        else if (v.Op == ssa.OpMIPSLoweredAtomicStore32) 
            op = mips.AMOVW;
                p = s.Prog(op);
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[1].Reg();
        p.To.Type = obj.TYPE_MEM;
        p.To.Reg = v.Args[0].Reg();

        s.Prog(mips.ASYNC);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredAtomicStorezero)
    {
        s.Prog(mips.ASYNC);

        p = s.Prog(mips.AMOVW);
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = mips.REGZERO;
        p.To.Type = obj.TYPE_MEM;
        p.To.Reg = v.Args[0].Reg();

        s.Prog(mips.ASYNC);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredAtomicExchange) 
    {
        // SYNC
        // MOVW Rarg1, Rtmp
        // LL    (Rarg0), Rout
        // SC    Rtmp, (Rarg0)
        // BEQ    Rtmp, -3(PC)
        // SYNC
        s.Prog(mips.ASYNC);

        p = s.Prog(mips.AMOVW);
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = v.Args[1].Reg();
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = mips.REGTMP;

        var p1 = s.Prog(mips.ALL);
        p1.From.Type = obj.TYPE_MEM;
        p1.From.Reg = v.Args[0].Reg();
        p1.To.Type = obj.TYPE_REG;
        p1.To.Reg = v.Reg0();

        p2 = s.Prog(mips.ASC);
        p2.From.Type = obj.TYPE_REG;
        p2.From.Reg = mips.REGTMP;
        p2.To.Type = obj.TYPE_MEM;
        p2.To.Reg = v.Args[0].Reg();

        p3 = s.Prog(mips.ABEQ);
        p3.From.Type = obj.TYPE_REG;
        p3.From.Reg = mips.REGTMP;
        p3.To.Type = obj.TYPE_BRANCH;
        p3.To.SetTarget(p);

        s.Prog(mips.ASYNC);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredAtomicAdd) 
    {
        // SYNC
        // LL    (Rarg0), Rout
        // ADDU Rarg1, Rout, Rtmp
        // SC    Rtmp, (Rarg0)
        // BEQ    Rtmp, -3(PC)
        // SYNC
        // ADDU Rarg1, Rout
        s.Prog(mips.ASYNC);

        p = s.Prog(mips.ALL);
        p.From.Type = obj.TYPE_MEM;
        p.From.Reg = v.Args[0].Reg();
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg0();

        p1 = s.Prog(mips.AADDU);
        p1.From.Type = obj.TYPE_REG;
        p1.From.Reg = v.Args[1].Reg();
        p1.Reg = v.Reg0();
        p1.To.Type = obj.TYPE_REG;
        p1.To.Reg = mips.REGTMP;

        p2 = s.Prog(mips.ASC);
        p2.From.Type = obj.TYPE_REG;
        p2.From.Reg = mips.REGTMP;
        p2.To.Type = obj.TYPE_MEM;
        p2.To.Reg = v.Args[0].Reg();

        p3 = s.Prog(mips.ABEQ);
        p3.From.Type = obj.TYPE_REG;
        p3.From.Reg = mips.REGTMP;
        p3.To.Type = obj.TYPE_BRANCH;
        p3.To.SetTarget(p);

        s.Prog(mips.ASYNC);

        p4 = s.Prog(mips.AADDU);
        p4.From.Type = obj.TYPE_REG;
        p4.From.Reg = v.Args[1].Reg();
        p4.Reg = v.Reg0();
        p4.To.Type = obj.TYPE_REG;
        p4.To.Reg = v.Reg0();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredAtomicAddconst) 
    {
        // SYNC
        // LL    (Rarg0), Rout
        // ADDU $auxInt, Rout, Rtmp
        // SC    Rtmp, (Rarg0)
        // BEQ    Rtmp, -3(PC)
        // SYNC
        // ADDU $auxInt, Rout
        s.Prog(mips.ASYNC);

        p = s.Prog(mips.ALL);
        p.From.Type = obj.TYPE_MEM;
        p.From.Reg = v.Args[0].Reg();
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg0();

        p1 = s.Prog(mips.AADDU);
        p1.From.Type = obj.TYPE_CONST;
        p1.From.Offset = v.AuxInt;
        p1.Reg = v.Reg0();
        p1.To.Type = obj.TYPE_REG;
        p1.To.Reg = mips.REGTMP;

        p2 = s.Prog(mips.ASC);
        p2.From.Type = obj.TYPE_REG;
        p2.From.Reg = mips.REGTMP;
        p2.To.Type = obj.TYPE_MEM;
        p2.To.Reg = v.Args[0].Reg();

        p3 = s.Prog(mips.ABEQ);
        p3.From.Type = obj.TYPE_REG;
        p3.From.Reg = mips.REGTMP;
        p3.To.Type = obj.TYPE_BRANCH;
        p3.To.SetTarget(p);

        s.Prog(mips.ASYNC);

        p4 = s.Prog(mips.AADDU);
        p4.From.Type = obj.TYPE_CONST;
        p4.From.Offset = v.AuxInt;
        p4.Reg = v.Reg0();
        p4.To.Type = obj.TYPE_REG;
        p4.To.Reg = v.Reg0();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredAtomicAnd || v.Op == ssa.OpMIPSLoweredAtomicOr) 
    {
        // SYNC
        // LL    (Rarg0), Rtmp
        // AND/OR    Rarg1, Rtmp
        // SC    Rtmp, (Rarg0)
        // BEQ    Rtmp, -3(PC)
        // SYNC
        s.Prog(mips.ASYNC);

        p = s.Prog(mips.ALL);
        p.From.Type = obj.TYPE_MEM;
        p.From.Reg = v.Args[0].Reg();
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = mips.REGTMP;

        p1 = s.Prog(v.Op.Asm());
        p1.From.Type = obj.TYPE_REG;
        p1.From.Reg = v.Args[1].Reg();
        p1.Reg = mips.REGTMP;
        p1.To.Type = obj.TYPE_REG;
        p1.To.Reg = mips.REGTMP;

        p2 = s.Prog(mips.ASC);
        p2.From.Type = obj.TYPE_REG;
        p2.From.Reg = mips.REGTMP;
        p2.To.Type = obj.TYPE_MEM;
        p2.To.Reg = v.Args[0].Reg();

        p3 = s.Prog(mips.ABEQ);
        p3.From.Type = obj.TYPE_REG;
        p3.From.Reg = mips.REGTMP;
        p3.To.Type = obj.TYPE_BRANCH;
        p3.To.SetTarget(p);

        s.Prog(mips.ASYNC);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredAtomicCas) 
    {
        // MOVW $0, Rout
        // SYNC
        // LL    (Rarg0), Rtmp
        // BNE    Rtmp, Rarg1, 4(PC)
        // MOVW Rarg2, Rout
        // SC    Rout, (Rarg0)
        // BEQ    Rout, -4(PC)
        // SYNC
        p = s.Prog(mips.AMOVW);
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = mips.REGZERO;
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg0();

        s.Prog(mips.ASYNC);

        p1 = s.Prog(mips.ALL);
        p1.From.Type = obj.TYPE_MEM;
        p1.From.Reg = v.Args[0].Reg();
        p1.To.Type = obj.TYPE_REG;
        p1.To.Reg = mips.REGTMP;

        p2 = s.Prog(mips.ABNE);
        p2.From.Type = obj.TYPE_REG;
        p2.From.Reg = v.Args[1].Reg();
        p2.Reg = mips.REGTMP;
        p2.To.Type = obj.TYPE_BRANCH;

        p3 = s.Prog(mips.AMOVW);
        p3.From.Type = obj.TYPE_REG;
        p3.From.Reg = v.Args[2].Reg();
        p3.To.Type = obj.TYPE_REG;
        p3.To.Reg = v.Reg0();

        p4 = s.Prog(mips.ASC);
        p4.From.Type = obj.TYPE_REG;
        p4.From.Reg = v.Reg0();
        p4.To.Type = obj.TYPE_MEM;
        p4.To.Reg = v.Args[0].Reg();

        p5 = s.Prog(mips.ABEQ);
        p5.From.Type = obj.TYPE_REG;
        p5.From.Reg = v.Reg0();
        p5.To.Type = obj.TYPE_BRANCH;
        p5.To.SetTarget(p1);

        s.Prog(mips.ASYNC);

        p6 = s.Prog(obj.ANOP);
        p2.To.SetTarget(p6);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredNilCheck) 
    {
        // Issue a load which will fault if arg is nil.
        p = s.Prog(mips.AMOVB);
        p.From.Type = obj.TYPE_MEM;
        p.From.Reg = v.Args[0].Reg();
        ssagen.AddAux(_addr_p.From, v);
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = mips.REGTMP;
        if (logopt.Enabled()) {
            logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name);
        }
        if (@base.Debug.Nil != 0 && v.Pos.Line() > 1) { // v.Pos.Line()==1 in generated wrappers
            @base.WarnfAt(v.Pos, "generated nil check");
        }
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSFPFlagTrue || v.Op == ssa.OpMIPSFPFlagFalse) 
    {
        // MOVW        $1, r
        // CMOVF    R0, r

        var cmov = mips.ACMOVF;
        if (v.Op == ssa.OpMIPSFPFlagFalse) {
            cmov = mips.ACMOVT;
        }
        p = s.Prog(mips.AMOVW);
        p.From.Type = obj.TYPE_CONST;
        p.From.Offset = 1;
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        p1 = s.Prog(cmov);
        p1.From.Type = obj.TYPE_REG;
        p1.From.Reg = mips.REGZERO;
        p1.To.Type = obj.TYPE_REG;
        p1.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredGetClosurePtr) 
    {
        // Closure pointer is R22 (mips.REGCTXT).
        ssagen.CheckLoweredGetClosurePtr(v);
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredGetCallerSP) 
    {
        // caller's SP is FixedFrameSize below the address of the first arg
        p = s.Prog(mips.AMOVW);
        p.From.Type = obj.TYPE_ADDR;
        p.From.Offset = -@base.Ctxt.FixedFrameSize();
        p.From.Name = obj.NAME_PARAM;
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpMIPSLoweredGetCallerPC)
    {
        p = s.Prog(obj.AGETCALLERPC);
        p.To.Type = obj.TYPE_REG;
        p.To.Reg = v.Reg();
        goto __switch_break0;
    }
    if (v.Op == ssa.OpClobber || v.Op == ssa.OpClobberReg)
    {
        goto __switch_break0;
    }
    // default: 
        v.Fatalf("genValue not implemented: %s", v.LongString());

    __switch_break0:;
}



private static void ssaGenBlock(ptr<ssagen.State> _addr_s, ptr<ssa.Block> _addr_b, ptr<ssa.Block> _addr_next) {
    ref ssagen.State s = ref _addr_s.val;
    ref ssa.Block b = ref _addr_b.val;
    ref ssa.Block next = ref _addr_next.val;


    if (b.Kind == ssa.BlockPlain) 
        if (b.Succs[0].Block() != next) {
            var p = s.Prog(obj.AJMP);
            p.To.Type = obj.TYPE_BRANCH;
            s.Branches = append(s.Branches, new ssagen.Branch(P:p,B:b.Succs[0].Block()));
        }
    else if (b.Kind == ssa.BlockDefer) 
        // defer returns in R1:
        // 0 if we should continue executing
        // 1 if we should jump to deferreturn call
        p = s.Prog(mips.ABNE);
        p.From.Type = obj.TYPE_REG;
        p.From.Reg = mips.REGZERO;
        p.Reg = mips.REG_R1;
        p.To.Type = obj.TYPE_BRANCH;
        s.Branches = append(s.Branches, new ssagen.Branch(P:p,B:b.Succs[1].Block()));
        if (b.Succs[0].Block() != next) {
            p = s.Prog(obj.AJMP);
            p.To.Type = obj.TYPE_BRANCH;
            s.Branches = append(s.Branches, new ssagen.Branch(P:p,B:b.Succs[0].Block()));
        }
    else if (b.Kind == ssa.BlockExit)     else if (b.Kind == ssa.BlockRet) 
        s.Prog(obj.ARET);
    else if (b.Kind == ssa.BlockRetJmp) 
        p = s.Prog(obj.ARET);
        p.To.Type = obj.TYPE_MEM;
        p.To.Name = obj.NAME_EXTERN;
        p.To.Sym = b.Aux._<ptr<obj.LSym>>();
    else if (b.Kind == ssa.BlockMIPSEQ || b.Kind == ssa.BlockMIPSNE || b.Kind == ssa.BlockMIPSLTZ || b.Kind == ssa.BlockMIPSGEZ || b.Kind == ssa.BlockMIPSLEZ || b.Kind == ssa.BlockMIPSGTZ || b.Kind == ssa.BlockMIPSFPT || b.Kind == ssa.BlockMIPSFPF) 
        var jmp = blockJump[b.Kind];
        p = ;

        if (next == b.Succs[0].Block()) 
            p = s.Br(jmp.invasm, b.Succs[1].Block());
        else if (next == b.Succs[1].Block()) 
            p = s.Br(jmp.asm, b.Succs[0].Block());
        else 
            if (b.Likely != ssa.BranchUnlikely) {
                p = s.Br(jmp.asm, b.Succs[0].Block());
                s.Br(obj.AJMP, b.Succs[1].Block());
            }
            else
 {
                p = s.Br(jmp.invasm, b.Succs[1].Block());
                s.Br(obj.AJMP, b.Succs[0].Block());
            }
                if (!b.Controls[0].Type.IsFlags()) {
            p.From.Type = obj.TYPE_REG;
            p.From.Reg = b.Controls[0].Reg();
        }
    else 
        b.Fatalf("branch not implemented: %s", b.LongString());
    }

} // end mips_package
